﻿
using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Runtime;
using netDxf.Entities;
using SkiaSharp;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LightCAD.MathLib;

namespace LightCAD.Drawing.Actions
{
    public class LineAction : Curve2dAction
    {
        private LineAction() { }

        public LineAction(IDocumentEditor docEditor): base(docEditor)
        {
            this.commandCtrl.WriteInfo("命令：Line");
        }


        private static readonly LcCreateMethod[] CreateMethods;

        static LineAction()
        {
            CreateMethods = new LcCreateMethod[1];         //这行代码创建了一个名为 'CreateMethods' 的静态数组，该数组的长度为1。这个数组似乎用于存储 'LcCreateMethod' 对象。
            CreateMethods[0] = new LcCreateMethod()       //在数组的第一个位置，也就是索引为0的位置，创建了一个 'LcCreateMethod' 对象。这个对象似乎用于描述某种创建方法，具体来说，这里描述了一个名为 "2P" 的创建方法，其描述为 "两点创建直线"。
            {
                Name = "2P",
                Description = "两点创建直线",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name = "Step0", Options = "指定第一个点:" },
                    new LcCreateStep { Name = "Step1", Options = "指定下一个点或[放弃(U)]:" },
                    new LcCreateStep { Name = "Step2", Options = "指定下一个点或[闭合(C) 放弃(U)]:" },
                }
            };
        }

        internal static void Initilize()
        {
            ElementActions.Line = new LineAction();
            LcDocument.ElementActions.Add(BuiltinElementType.Line, ElementActions.Line);
        }

        private List<Vector2> points;

        private Vector2 firstPoint { get; set; }

        private Vector2 secondPoint { get; set; }

        private PointInputer inputer { get; set; }


        public async void ExecCreate(string[] args = null)
        {
            this.StartCreating();
            DocumentManager.CurrentRecorder.BeginAction("Line");

            this.points = new List<Vector2>();

            this.inputer = new PointInputer(this.docEditor);
            var curMethod = CreateMethods[0];

        Step0:
            var step0 = curMethod.Steps[0];
            var result0 = await inputer.Execute(step0.Options);
            //var result0 = await inputer.Execute(step0.Options);

            if (inputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }

            // zcb: 增加Res0为空的判定
            if (result0 == null)
            {
                this.Cancel();
                goto End;
            }

            if (result0.ValueX == null)
            {
                if (result0.Option != null)
                {
                    //TODO:AutoCAD画线输入一个数字，是怎么确定点的？
                }
                else
                {
                    
                }
            }
            this.firstPoint = (Vector2)result0.ValueX;
 
        Step1:
            var step1 = curMethod.Steps[1];
            if (this.points.Count >= 2)
            {
                step1 = curMethod.Steps[2];
            }
            var result1 = await inputer.Execute(step1.Options);
            if (inputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }

            if (result1.Option != null)
            {
                if (result1.Option == "U") 
                {
                    goto End;
                }
                else if (result1.Option == "C")
                {
                    //没有实现
                    goto End;
                }
                else if (result1.Option == " ")
                {
                    goto End;
                }
                else if (result1.ValueX != null)
                {
                    Vector2 pointB = (Vector2)result1.ValueX;
                    Vector2 direction = (pointB - firstPoint).Normalize();
                    //   result1 = firstPoint + direction * double.Parse(result1.Option) / this.vportRt.Viewport.Scale;
                    result1.ValueX = firstPoint + direction * double.Parse(result1.Option);
                }
                else
                {
                    goto Step1;
                }
            }
            this.secondPoint = (Vector2)result1.ValueX;
 

            points.Add(firstPoint);
            points.Add(secondPoint);
            this.vportRt.ActiveElementSet.AddLine(firstPoint, secondPoint);
            firstPoint = secondPoint;

        End:
            this.inputer = null;
            this.EndCreating();

            if (this.points.Count > 0)
            {
                DocumentManager.CurrentRecorder.EndAction();
            }
            else
            {
                DocumentManager.CurrentRecorder.CancleAction();
            }
        }

        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
        }

        public void CreateElements()
        {
            for (var i = 0; i < this.points.Count; i += 2)
            {
                var pre = this.points[i];
                var p = this.points[i + 1];
                this.docRt.Document.ModelSpace.AddLine(pre, p);
            }
        }

        public override void DrawAuxLines(SKCanvas canvas)
        {
            if (firstPoint != null)
            {
                var mp = this.vportRt.PointerMovedPosition.ToVector2d();
                var wcs_mp = this.vportRt.ConvertScrToWcs(mp);
                //DrawAuxLine(canvas, firstPoint, wcs_mp, Constants.auxElementPen);
                DrawAuxLine(canvas, firstPoint, wcs_mp, new SKPaint { Color = this.vportRt.GetAuxColorValue(), IsStroke = true });
                //var Xdifference = Math.Abs(wcs_mp.X - firstPoint.X);

                //LcXLine Ytop = new LcXLine(firstPoint, new Vector2d(firstPoint.X, firstPoint.Y + 500000));
                //LcXLine YBootom = new LcXLine(firstPoint, new Vector2d(firstPoint.X, firstPoint.Y - 500000));
                //LcXLine Xleft = new LcXLine(firstPoint, new Vector2d(firstPoint.X - 500000, firstPoint.Y ));
                //LcXLine XRight = new LcXLine(firstPoint, new Vector2d(firstPoint.X +500000, firstPoint.Y ));

                //var ErrValue = 500;
                //if (GeoUtils.PointDistanceToLine(wcs_mp,Ytop.StartPoint, Ytop.Direction)< ErrValue && Ytop.StartPoint.Y<wcs_mp.Y)
                //{
                //    var box = this.vportRt.Renderer.GetWcsClipBox();
                //    var interPs = GeoUtils.IntersectXLineBox(Ytop, box);
                //    DrawAuxLine(canvas, Ytop.StartPoint, interPs[0], Constants.dottedlinePen);
                //}
                //else if (GeoUtils.PointDistanceToLine(wcs_mp, YBootom.StartPoint, YBootom.Direction) < ErrValue && Ytop.StartPoint.Y > wcs_mp.Y)
                //{
                //    var box = this.vportRt.Renderer.GetWcsClipBox();
                //    var interPs = GeoUtils.IntersectXLineBox(YBootom, box);
                //    DrawAuxLine(canvas, YBootom.StartPoint, interPs[1], Constants.dottedlinePen);
                //}
                //else if (GeoUtils.PointDistanceToLine(wcs_mp, Xleft.StartPoint, Xleft.Direction) < ErrValue && Xleft.StartPoint.X < wcs_mp.X)
                //{
                //    var box = this.vportRt.Renderer.GetWcsClipBox();
                //    var interPs = GeoUtils.IntersectXLineBox(Xleft, box);
                //    if (interPs.Count==0)
                //    {
                //        return;
                //    }
                //    DrawAuxLine(canvas, Xleft.StartPoint, interPs[0], Constants.dottedlinePen);
                //}
                //else if (GeoUtils.PointDistanceToLine(wcs_mp, XRight.StartPoint, XRight.Direction) < ErrValue && XRight.StartPoint.X >  wcs_mp.X)
                //{
                //    var box = this.vportRt.Renderer.GetWcsClipBox();
                //    var interPs = GeoUtils.IntersectXLineBox(XRight, box);
                //    DrawAuxLine(canvas, XRight.StartPoint, interPs[1], Constants.dottedlinePen);
                //}

            }
        }

        //public override void DrawTemp(SKCanvas canvas)
        //{
        //    for (var i = 1; i < this.points.Count; i++)
        //    {
        //        var pre = this.points[i - 1];
        //        var p = this.points[i];
        //        DrawLine(canvas, pre, p);
        //    }
        //}
        private void DrawAuxLine(SKCanvas canvas, Vector2 p0, Vector2 p1, SKPaint sKPaint)
        {
            var sk_pre = this.vportRt.ConvertWcsToScr(p0).ToSKPoint();
            var sk_p = this.vportRt.ConvertWcsToScr(p1).ToSKPoint();
            //辅助元素的颜色 
            canvas.DrawLine(sk_pre, sk_p, sKPaint);
            //辅助曲线的颜色，包括辅助长度，辅助角度等
        }
        private void DrawVectorline(SKCanvas canvas, Vector2 p0, Vector2 xlineDir)
        {

            var box = this.vportRt.Renderer.GetWcsClipBox();
            var xline = new LcXLine(p0, xlineDir);

            var interPs = LcGeoUtils.IntersectXLineBox(xline, box);

            if (interPs.Count == 0)
            {
                return;
            }
            //var interP = interPs.OrderByDescending(P => Vector2d.Distance(p0, P)).FirstOrDefault();
            var sk_pre = this.vportRt.ConvertWcsToScr(p0).ToSKPoint();
            var sk_p = this.vportRt.ConvertWcsToScr(interPs[0]).ToSKPoint();
            canvas.DrawLine(sk_pre, sk_p, Constants.draggingPen);

        }

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var line = element as LcLine; //getHel
            var grips = new List<ControlGrip>();
            var gripStart = new ControlGrip
            {
                Element = line,
                Name = "Start",
                Position = line.Start
            };
            grips.Add(gripStart);
            var gripEnd = new ControlGrip
            {
                Element = line,
                Name = "End",
                Position = line.End
            };
            grips.Add(gripEnd);
            return grips.ToArray();
        }

        private string _gripName;

        private Vector2 _position;

        private LcLine _line;
        public Vector2 PrePoint { get; set; }
        public override void SetDragGrip(LcElement element, string gripName, Vector2 position, bool isEnd)
        {
            var line = element as LcLine;
            _line = line;


            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
            }
            else
            {
                Vector2 dragpoint = position;

                if (this.vportRt.SnapRt?.Current != null)
                {
                    dragpoint = this.vportRt.SnapRt.Current.SnapPoint;
                }
                if (gripName == "Start")
                {
                    line.Set(start: dragpoint);
                }
                else if (gripName == "End")
                {
                    line.Set(end: dragpoint);
                }
            }

            //  DocumentManager.CurrentRecorder.EndAction();
        }

        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_line == null) return;

            var start = this.vportRt.ConvertWcsToScr(_line.Start);
            if (_gripName == "Start")
                start = this.vportRt.ConvertWcsToScr(_position);

            var end = this.vportRt.ConvertWcsToScr(_line.End);
            if (_gripName == "End")
                end = this.vportRt.ConvertWcsToScr(_position);

            canvas.DrawLine(start.ToSKPoint(), end.ToSKPoint(), Constants.draggingPen);
        }

        public override List<PropertyObserver> GetPropertyObservers()
        {
            return new List<PropertyObserver>()
                       {
                           new PropertyObserver()
                               {
                                    Name = "StartX",
                                    DisplayName = "起点 X 坐标",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).Start.X,
                                    Setter = (ele, value) =>
                                       {
                                           var line = (ele as LcLine);
                                           var x = Convert.ToDouble(value);
                                           var sp = new Vector2(x, line.Start.Y);
                                           line.Set(start: sp);
                                       }
                               },
                           new PropertyObserver()
                               {
                                    Name = "StartY",
                                    DisplayName = "起点 Y 坐标",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).Start.Y,
                                    Setter = (ele, value) =>
                                       {
                                           var line = (ele as LcLine);
                                           var y = Convert.ToDouble(value);
                                           var sp = new Vector2(line.Start.X, y);
                                           line.Set(start: sp);
                                       }
                               },
                           new PropertyObserver()
                               {
                                    Name = "EndX",
                                    DisplayName = "端点 X 坐标",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).End.X,
                                    Setter = (ele, value) =>
                                       {
                                           var line = (ele as LcLine);
                                           var x = Convert.ToDouble(value);
                                           var ep = new Vector2(x, line.End.Y);
                                           line.Set(end: ep);
                                       }
                               },
                           new PropertyObserver()
                               {
                                    Name = "EndY",
                                    DisplayName = "端点 Y 坐标",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).End.Y,
                                    Setter = (ele, value) =>
                                       {
                                           var line = (ele as LcLine);
                                           var y = Convert.ToDouble(value);
                                           var ep = new Vector2(line.End.X, y);
                                           line.Set(end: ep);
                                       }
                               },
                           new PropertyObserver()
                               {
                                    Name = "DeltaX",
                                    DisplayName = "增量X",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).DeltaX
                               },
                           new PropertyObserver()
                               {
                                    Name = "DeltaY",
                                    DisplayName = "增量Y",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).DeltaY
                               },
                           new PropertyObserver()
                               {
                                    Name = "Length",
                                    DisplayName = "长度",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).Length
                               },
                           new PropertyObserver()
                               {
                                    Name = "Angle",
                                    DisplayName = "角度",
                                    CategoryName = "Geometry",
                                    CategoryDisplayName = "几何图形",
                                    Getter = (ele) => (ele as LcLine).Angle * 180 / Math.PI
                               }
                       };
        }


        public override SnapPointResult SnapPoint(SnapRuntime snapRt, LcElement element, Vector2 point, bool forRef, Vector2 PrePoint=null)
        {

            var maxDistance = vportRt.GetSnapMaxDistance();
            var line = element as LcLine;
            var sscur = SnapSettings.Current;
            var result = new SnapPointResult { Element = element };
            var line2d = line.Line;

            if (sscur.ObjectOn)
            {

                if (sscur.PointType.Has(SnapPointType.Endpoint))
                {
                    if ((point - line.Start).Length() <= maxDistance)
                    {
                        result.Point = line.Start;
                        result.Name = "Start";
                        result.Curves.Add(new SnapRefCurve(SnapPointType.Endpoint, line2d));
                    }
                    else if ((point - line.End).Length() <= maxDistance)
                    {
                        result.Point = line.End;
                        result.Name = "End";
                        result.Curves.Add(new SnapRefCurve(SnapPointType.Endpoint, line2d));
                    }
                }
                if (sscur.PointType.Has(SnapPointType.Midpoint) && result.Point == null)
                {
                    var cp = (line.Start + line.End) / 2;
                    if ((point - cp).Length() <= maxDistance)
                    {
                        result.Point = cp;
                        result.Name = "Mid";
                        result.Curves.Add(new SnapRefCurve(SnapPointType.Midpoint, line2d));
                    }
                }
                //forRef时只捕捉端点，中点等，如果
                if (!forRef && result.Point == null)
                {
                    var refPoints = snapRt.GetRefPoints(element);
                    var lineLength = (line.End - line.Start).Length();
                    var distance = GeoUtils.PointToLineDistance(line.Start, line.End, point, out Vector2 nearest, out double dirDistance);
                    Vector2 Cross = null;
                    if (PrePoint != null)
                    {

                        Cross = GeoUtils.FindFoot(line.Start, line.End, PrePoint);
                    }
                 
                    if (sscur.PointType.Has(SnapPointType.Nearest))
                    {

                        if (Cross != null)
                        {
                            if (Vector2.Distance(Cross, point) < maxDistance)
                            {
                                result.Point = Cross;
                                result.Name = "FootPoint";
                                result.Curves.Add(new SnapRefCurve(SnapPointType.Perpendicular, line2d));
                                return result;
                            }
                        }
                        //最近点必须在线段内进行测试
                        if (distance < maxDistance && dirDistance > 0 && dirDistance < lineLength)
                        {
                            result.Point = nearest;
                            result.Name = "Nearest";
                            result.Curves.Add(new SnapRefCurve(SnapPointType.Nearest, line2d));
                        }
                    }
                    //进行延长线上点捕捉，需要元素上有参考点
                    if (sscur.PointType.Has(SnapPointType.ExtensionLine) && refPoints.Count > 0 && result.Point == null)
                    {
                        if (Cross != null)
                        {
                            if (Vector2.Distance(Cross, point) < maxDistance)
                            {
                                result.Point = Cross;
                                result.Name = "FootPoint";
                                result.Curves.Add(new SnapRefCurve(SnapPointType.Perpendicular, line2d));
                                return result;
                            }
                        }
                        if (distance < maxDistance)
                        {
                            result.Point = nearest;
                            result.Name = "Extension";
                            result.Curves.Add(new SnapRefCurve(SnapPointType.ExtensionLine, line2d));
                        }
                    }
                }
            }

            if (result.Point != null)
                return result;
            else
                return null;
        }

        public override SnapPointResult SnapLine(SnapRuntime snapRt, List<Vector2> vector2s, Vector2 point, bool forRef)
        {

            var maxDistance = vportRt.GetSnapMaxDistance();
            var sscur = SnapSettings.Current;
            var result = new SnapPointResult { };
            if (sscur.ObjectOn)
            {

                if (sscur.PointType.Has(SnapPointType.Intersection))
                {
                    foreach (Vector2 vector2 in vector2s)
                    {
                        if ((point - vector2).Length() <= maxDistance)
                        {
                            result.Point = vector2;
                            result.Name = "Intersection";
                            result.Curves.Add(new SnapRefCurve(SnapPointType.Intersection, new Line2d(vector2, vector2)));
                        }
                    }
                }
            }
            if (result.Point != null)
                return result;
            else
                return null;
        }
    }
}